Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 | export const dynamic = "force-dynamic"; /** * Admin Ticket Messages API * POST /api/admin/support/tickets/[id]/messages - Add agent message */ import { NextRequest, NextResponse } from 'next/server'; import { requireAdminRole, handleAuthError } from '@/lib/auth'; import { prisma } from '@/lib/prisma'; import { CreateMessageSchema } from '@/lib/validation/support-schemas'; import { notifyNewMessage } from '@/lib/support/notification-utils'; import { TicketStatus, MessageSenderType, SupportTicketWithRelations } from '@/types/support'; import { handleError } from '@/lib/error-handler'; import { logger } from '@/lib/logging'; interface RouteParams { params: Promise<{ id: string }>; } export async function POST(request: NextRequest, { params }: RouteParams) { try { const session = await requireAdminRole(); const { id } = await params; const agentId = Number(session.user.id); const agentName = session.user.name || 'Support Agent'; // Verify ticket exists const ticket = await prisma.supportTicket.findUnique({ where: { id }, select: { id: true, ticketNumber: true, status: true, userId: true, firstResponseAt: true, assignedToId: true}}); if (!ticket) { return NextResponse.json( { success: false, error: 'Ticket not found' }, { status: 404 } ); } // Validate input const body = await request.json(); const validationResult = CreateMessageSchema.safeParse(body); if (!validationResult.success) { return NextResponse.json( { success: false, error: 'Validation failed', details: validationResult.error.flatten().fieldErrors}, { status: 400 } ); } const data = validationResult.data; // Create the message const message = await prisma.supportMessage.create({ data: { ticketId: id, senderType: MessageSenderType.AGENT, senderId: agentId, senderName: agentName, content: data.content, isInternal: data.isInternal || false}, include: { sender: { select: { id: true, name: true }}, attachments: true}}); // Build ticket update data const ticketUpdate: Record<string, unknown> = {}; // Set first response time if not already set if (!ticket.firstResponseAt && !data.isInternal) { ticketUpdate.firstResponseAt = new Date(); } // Auto-assign to responding agent if not assigned if (!ticket.assignedToId) { ticketUpdate.assignedToId = agentId; } // Update status if not an internal note if (!data.isInternal) { // If OPEN or AWAITING_AGENT, move to AWAITING_CUSTOMER if ( ticket.status === TicketStatus.OPEN || ticket.status === TicketStatus.AWAITING_AGENT ) { ticketUpdate.status = TicketStatus.AWAITING_CUSTOMER; } else if (ticket.status === TicketStatus.IN_PROGRESS) { // Keep IN_PROGRESS but we could also move to AWAITING_CUSTOMER ticketUpdate.status = TicketStatus.AWAITING_CUSTOMER; } } // Update ticket if there are changes if (Object.keys(ticketUpdate).length > 0) { await prisma.supportTicket.update({ where: { id }, data: ticketUpdate}); } // Notify customer if not an internal note if (!data.isInternal && ticket.userId) { const ticketForNotify = { ...ticket, id, assignedToId: ticket.assignedToId || agentId} as unknown as SupportTicketWithRelations; await notifyNewMessage(ticketForNotify, agentName, data.isInternal); } logger.info('Agent message added to ticket', { category: 'ADMIN_SUPPORT', ticketId: id, messageId: message.id, agentId, isInternal: data.isInternal}); return NextResponse.json( { success: true, data: message}, { status: 201 } ); } catch (error) { const authResponse = handleAuthError(error as Error); if (authResponse) return authResponse; logger.error('Error adding agent message', error instanceof Error ? error : new Error(String(error)), { category: 'ADMIN_SUPPORT' }); return handleError(error); } } |